package pck.ivrGraph.models;

import org.jgraph.graph.*;
import pck.ivrGraph.models.flows.IvrFlow;
import pck.ivrGraph.models.flows.IvrMacro;
import pck.ivrGraph.models.flows.IvrPrompt;
import pck.ivrGraph.models.flows.IvrComponent;

import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;


public class JGModel extends ArrayList<DefaultGraphCell>{

	private static final int START_X = 0;
	private static final int START_Y = 40;
	private static final int NODE_WIDTH = 150;
	private  static final int NODE_HIGHT = 20;
	private  static final int GAP_BETWEEN_NODES = 20;

	private DBModel dbModel;

	private ArrayList<DefaultGraphCell> cells = new ArrayList<DefaultGraphCell>();
	private Integer shift = new Integer(0);
	private ArrayList<String> visitedFlows = new ArrayList<String>();
	private HashMap<IvrFlow, Integer> flowWeight = new HashMap<IvrFlow, Integer>();
	private HashMap<IvrFlow, Integer> parentStartCoord = new HashMap<IvrFlow, Integer>();

	public JGModel() throws SQLException {
		super();
		dbModel = new DBModel();
		fillCellList(dbModel.getRootFlow(), 0);
	}

	private DefaultGraphCell fillCellList(IvrFlow flow, int depthY){
		DefaultGraphCell cell = new DefaultGraphCell(flow);
		cells.add(cell);
		cell.setAttributes(getNodeAttributes(flow));
		Set childs = flow.getKeys();
		if(childs != null && childs.size() > 0){
			parentStartCoord.put(flow, shift);
			Iterator iter = childs.iterator();
			DefaultPort port;
			while(iter.hasNext()){
				IvrFlow child = flow.getChildFlow((String)iter.next());

				port = new DefaultPort();
				cell.add(port);
				DefaultEdge edge = new DefaultEdge();
				edge.setAttributes(getEdgeAttributes());
				edge.setSource(port);

				if(child != flow){
					port = new DefaultPort();
					DefaultGraphCell childCell = findExistingCell(child);
					if(childCell == null){
						childCell = fillCellList(child, depthY+1);
						int weight = (flowWeight.get(flow) == null ? 0 : flowWeight.get(flow)) + flowWeight.get(child);
						flowWeight.put(flow, weight);
					}
					childCell.add(port);
				}else{
					cell.add(port);
				}

				edge.setTarget(port);
				this.add(edge);
			}
			int gap = 0; 
			if(flowWeight.get(flow) == null){
				flowWeight.put(flow, NODE_WIDTH);
				shift += NODE_WIDTH + GAP_BETWEEN_NODES;
				gap = GAP_BETWEEN_NODES;
			}
			Rectangle2D rect = new Rectangle2D.Double(START_X + flowWeight.get(flow)/2 - NODE_WIDTH/2 + parentStartCoord.get(flow) + gap ,
																								START_Y+NODE_HIGHT*depthY + 20*depthY,
																								NODE_WIDTH, NODE_HIGHT );
			GraphConstants.setBounds( cell.getAttributes(), rect);
		}else{
			if(!wasFlowVisited(flow.getName())){
				Rectangle2D rect = new Rectangle2D.Double( START_X + shift, START_Y+NODE_HIGHT*depthY + 20*depthY, NODE_WIDTH, NODE_HIGHT );
				GraphConstants.setBounds( cell.getAttributes(), rect);
				shift += NODE_WIDTH + GAP_BETWEEN_NODES;
				flowWeight.put(flow, NODE_WIDTH);
			}
		}

		visitedFlows.add(flow.getName());
		this.add(cell);
		return cell;
	}

	private boolean wasFlowVisited(String flowName){
		for(int i = 0; i < visitedFlows.size(); i++){
			if(flowName.equals(visitedFlows.get(i))){
				return true;
			}
		}
		return false;
	}

	private DefaultGraphCell findExistingCell(IvrFlow flow){
		for(int i = 0; i < cells.size(); i++){
			IvrFlow existingFlow = (IvrFlow)cells.get(i).getUserObject();
			if(flow.equals(existingFlow)){
				return cells.get(i);
			}
		}
		return null; 
	}

	private AttributeMap getNodeAttributes(IvrFlow component){
		AttributeMap attrs = new AttributeMap();

		if(component instanceof IvrComponent){
			GraphConstants.setGradientColor(attrs, Color.green);
		}else if(component instanceof IvrPrompt){
			GraphConstants.setGradientColor(attrs, Color.blue);
		}else if(component instanceof IvrMacro){
			GraphConstants.setGradientColor(attrs, Color.red);
		}

		GraphConstants.setMoveable(attrs, true);
		GraphConstants.setSizeable(attrs, false);
		GraphConstants.setOpaque(attrs, true);
		GraphConstants.setDisconnectable(attrs, false);
		GraphConstants.setEditable(attrs, false);
		GraphConstants.setSelectable(attrs, true);

		return attrs;
	}

	private AttributeMap getEdgeAttributes(){
		AttributeMap attrs = new AttributeMap();

		GraphConstants.setLineEnd(attrs, GraphConstants.ARROW_CLASSIC);
		GraphConstants.setEndFill(attrs, true);
		GraphConstants.setMoveable(attrs, false);
		GraphConstants.setBendable(attrs, true);
		GraphConstants.setSelectable(attrs, false);

		return attrs;
	}
}
